package com.ruicar.afs.cloud.risk.tortoise.components;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.annotation.JSONField;
import com.ruicar.afs.cloud.common.core.exception.AfsBaseException;
import com.ruicar.afs.cloud.risk.tortoise.anno.RefData;
import com.ruicar.afs.cloud.risk.tortoise.consts.GwtTortoiseConst;
import com.ruicar.afs.cloud.risk.tortoise.dto.response.GwtRefData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * @Description
 * @Author Peng Jinbo
 * @Date 2020/07/30 14:25
 */
@Component
@Slf4j
public class GwtRefDataFieldsCache {

    /**
     * REF_FIELD_CACHE
     */
    private static final Map<Class<? extends GwtRefData>, List<GwtRefDataField>> REF_FIELD_CACHE = new HashMap<>(64);

    /**
     * Author Peng Jinbo
     * Get list
     *
     * @param clz clz
     * @return the list
     */
    public static final List<GwtRefDataField> get(Class clz){
        return REF_FIELD_CACHE.get(clz);
    }

    /**
     * Author Peng Jinbo
     * Contains boolean
     *
     * @param clz clz
     * @return the boolean
     */
    public static final boolean contains(Class clz){
        return REF_FIELD_CACHE.containsKey(clz) && CollectionUtil.isNotEmpty(REF_FIELD_CACHE.get(clz));
    }

    /**
     * Author Peng Jinbo
     * Init
     */
    @PostConstruct
    public void init() {
        Set<Class<?>> classes = ClassUtil.scanPackage(GwtTortoiseConst.TORTOISE_DTO_PACKAGE_PATH ,
                clz -> GwtRefData.class.isAssignableFrom(clz) && clz != GwtRefData.class
        );
        for (Class<?> clz : classes) {
            addCache(clz);
        }
    }

    /**
     * Author Peng Jinbo
     * Add cache *
     *
     * @param clz clz
     */
    private void addCache(Class clz) {
        try {
            if(REF_FIELD_CACHE.containsKey(clz)) return;
            List<GwtRefDataField> fieldAndKeys = new ArrayList<>();
            Field[] fields = ReflectUtil.getFields(clz);
            for (Field field : fields) {
                JSONField jsonField = field.getAnnotation(JSONField.class);
                String jsonKey = Objects.nonNull(jsonField) ? jsonField.name() : "";
                if (field.isAnnotationPresent(RefData.class)) {
                    RefData refData = field.getAnnotation(RefData.class);
                    fieldAndKeys.add(GwtRefDataField.builder()
                            .dicKey(refData.dicKey())
                            .propertyDescriptor(new PropertyDescriptor(field.getName(), clz))
                            .defaultVal(refData.defaultVal())
                            .jsonKey(jsonKey)
                            .translatedKey(refData.translatedKey())
                            .useName(refData.useFieldName())
                            .whetherDesc(refData.whetherDesc())
                            .translatedByKey(refData.translatedByKey())
                            .build());
                } else if (GwtTortoiseConst.FIELD_NAME_REF_DATA.equals(field.getName())) {
                    fieldAndKeys.add(GwtRefDataField.builder()
                            .jsonKey(jsonKey)
                            .propertyDescriptor(new PropertyDescriptor(field.getName(), clz)).build());
                } else if (GwtRefData.class.isAssignableFrom(field.getType())) {
                    fieldAndKeys.add(GwtRefDataField.builder()
                            .jsonKey(jsonKey)
                            .entity(true)
                            .propertyDescriptor(new PropertyDescriptor(field.getName(), clz)).build());
                    addCache(field.getType());
                } else if (List.class.isAssignableFrom(field.getType())) {
                    ParameterizedType genericType = (ParameterizedType) field.getGenericType();
                    Class<?> factType = (Class<?>) genericType.getActualTypeArguments()[0];
                    if (!ClassUtil.isSimpleValueType(factType)) {
                        fieldAndKeys.add(GwtRefDataField.builder()
                                .propertyDescriptor(new PropertyDescriptor(field.getName(), clz))
                                .list(true)
                                .listParameterType(factType)
                                .build());
                        addCache(factType);
                    }
                }
                if (fieldAndKeys.size() > 1)
                    REF_FIELD_CACHE.put(clz, fieldAndKeys);
            }
        } catch (Exception e) {
            log.error("",e);
            throw new AfsBaseException("玄武数据对象字典翻译字段缓存初始化失败");
        }
    }


}
